package com.lzy.spring;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 自定义spring容器
 */
public class LzyApplicationContext {

	private Class configClass;

	// 保存BeanDefinition对象
	private ConcurrentMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

	private ConcurrentMap<String, Object> singletonMap = new ConcurrentHashMap<>(); // 这里存储的都是实例化了的对象。

	// 记录实现了BeanPostProcessor接口的对象
	private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

	public LzyApplicationContext(Class configClass) {
		this.configClass = configClass;

		// 获取扫描路径,扫描结果，是单例Bean，创建容器的情况就进行创建。
		if (this.configClass.isAnnotationPresent(ComponentScan.class)) {

			ComponentScan componentScanAnnotation = (ComponentScan)this.configClass.getAnnotation(ComponentScan.class);
			String pathSource = componentScanAnnotation.value(); // 扫描路径 com.lzy.service

			String path = pathSource.replace(".","/");  // com/lzy/service

			// 获取类加载器，进行.class文件的获取
			ClassLoader classLoader = LzyApplicationContext.class.getClassLoader();

			// resource ===>  file:/D:/program_workspace/spring_implement/target/classes/com/lzy/service
			URL resource = classLoader.getResource(path);  // 需要的是相对路径 D:\program_workspace\spring_implement\target\classes + \com\lzy\service

			List<Class> clazzList = new ArrayList<>(); // 保存Bean的类路径地址

			// 获取了.class的所在报下，那么就可以进行遍历，将不是字节码的文件进行过滤
			String file = resource.getFile();
			File files = new File(file);
			// 遍历处理文件夹
			recursionFile(files, pathSource, clazzList);

			// 遍历类路径集合，处理Bean
			for (Class clazz : clazzList) {

				// 判断是否含有Bean组件Component注解
				if (clazz.isAnnotationPresent(Component.class)) {

					if(BeanPostProcessor.class.isAssignableFrom(clazz)) {
						try {
							beanPostProcessorList.add((BeanPostProcessor)clazz.getConstructor().newInstance());
						} catch (Exception e) {
							e.printStackTrace();
						}
					}

					Component component = (Component) clazz.getAnnotation(Component.class);
					String beanName = component.value();

					// 如果没有写的话，就默认使用类名首字母小写
					if("".equals(beanName)) {
						beanName = clazz.getName().substring(clazz.getName().lastIndexOf(".")+1); // 这个只是截取类名字
						beanName = Introspector.decapitalize(beanName);
//						beanName.toLowerCase();
						System.out.println(beanName);
					}

					BeanDefinition beanDefinition = new BeanDefinition();
					beanDefinition.setType(clazz);

					// 判断Bean的作用域，没有的话抛出异常。
					if (clazz.isAnnotationPresent(Scope.class)) {
						Scope scope = (Scope) clazz.getAnnotation(Scope.class);
						String scopeValue = scope.value();

						if("prototype".equals(scopeValue)) {
							beanDefinition.setScope("prototype");
						} else if("singleton".equals(scopeValue)) {
							beanDefinition.setScope("singleton");
						} else {
							throw new RuntimeException("Does not support the current scope of " + scopeValue +" ");
						}
					} else { // // 没有Scope的话是默认单例Bean,默认单例Bean
						beanDefinition.setScope("singleton");
					}

					// 添加到Map中
					beanDefinitionMap.put(beanName, beanDefinition);
				}
			}
			
			// 实例化单例Bean，存储单例Bean
			for (String beanName : beanDefinitionMap.keySet()) {
				BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
				if("singleton".equals(beanDefinition.getScope())) {
					Object object = createBean(beanName, beanDefinition);
					singletonMap.put(beanName, object);
				}
			}
			
		}
	}

	/**
	 * 创建单例Bean
	 * @param beanName
	 * @param beanDefinition
	 * @return
	 */
	private Object createBean(String beanName, BeanDefinition beanDefinition) {
		// 1.获取Bean的类型
		Class clazz = beanDefinition.getType();

		System.out.println(clazz);
		// 2.使用反射获取Bean对象。
		try {
			Object beanInstance = clazz.getConstructor().newInstance();

			// 给对象进行赋值。存在@Autowired注解的话，就进行自动注入。
			for (Field field : clazz.getDeclaredFields()) {
				if (field.isAnnotationPresent(Autowired.class)) { // 是自动注入属性
					field.setAccessible(true); // 将私有属性设置为可以设置
					// 依赖注入先ByType再ByName
					field.set(beanInstance, getBean(field.getName()));

					// Aware回调，对于许多的回调 机制也是类似的思路。
					if(beanInstance instanceof BeanNameAware) {
						((BeanNameAware) beanInstance).setBeanName(beanName);
					}
				}
			}

			// 初始化前进行处理
			for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
				beanInstance = beanPostProcessor.postProcessBeforeInitialization(beanName, beanInstance);
			}

			// 初始化
			if(beanInstance instanceof InitializingBean) {
				((InitializingBean) beanInstance).afterPropertiesSet();
			}

			// 初始化后进行处理
			for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
				beanInstance = beanPostProcessor.postProcessAfterInitialization(beanName, beanInstance);
			}

			return beanInstance;
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}

		return null;
	}



	/**
	 * 向外提供一个获取Bean的方法
	 * @param beanName 获取的Bean的名字
	 * @return
	 */
	public Object getBean(String beanName) {
		// 1.从beanDefinitionMap中获取Beanfinition对象
		BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

		// 2.判断是否为单例 （处理获取单例对象的许多操作）
		if(beanDefinition == null) {
			throw new NullPointerException();
		} else {
			String scope = beanDefinition.getScope();
			if("singleton".equals(scope)) {
				Object beanBefore = singletonMap.get(beanName);
				if(beanBefore == null) { // 防止意外分支情况，需要当前danliBean，但是单例Bean还未创建
					Object bean = createBean(beanName, beanDefinition);
					// 保存到单例池中
					singletonMap.put(beanName, bean);
					return bean;
				}
				return beanBefore;
			} else {
				return createBean(beanName, beanDefinition);
			}
		}
	}


	/**
	 * 获取路径下的.class文件
	 * @param file
	 * @param path
	 * @param clazzList
	 */
	public void recursionFile(File file, String path, List<Class> clazzList) {
		if(file.isFile()) { // 是一个文件不用进行递归
			String filePath = file.getAbsolutePath(); // 获取绝对路径，加上了盘符
			if(filePath.endsWith(".class")) { // 说明以.class结尾的文件是我们当前所需要找的
				// 获取类的全限定类名
				String fullyClassName = path.replace(".class","");  // 例子： com.lzy.service.UserService.class

				try {
					Class<?> clazz = this.getClass().getClassLoader().loadClass(fullyClassName); // 需要获取的全限定类名 com.lzy.service.UserService
					// System.out.println(clazz);  //com.lzy.service.UserService
					clazzList.add(clazz);
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
			}

//			System.out.println(file.getName());
		} else { // 是一个文件夹需要进行递归获取文件
			File[] files = file.listFiles();
			for (File f : files) {
				recursionFile(f, path + "." + f.getName(), clazzList);
			}
		}
	}
}
